package com.example.sanguogameserver.ai.logic;


import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSON;
import com.example.sanguogameserver.ai.enums.AiType;
import com.example.sanguogameserver.ai.enums.GeneralStatusType;
import com.example.sanguogameserver.ai.enums.TreeType;
import com.example.sanguogameserver.ai.model.Position;
import com.example.sanguogameserver.ai.model.TreeGeneral;
import com.example.sanguogameserver.ai.model.TreeSkill;
import com.example.sanguogameserver.consts.SanGuoConst;
import com.example.sanguogameserver.dto.CastSkillRespDTO;
import com.example.sanguogameserver.dto.battle.AiRoundInfo;
import com.example.sanguogameserver.dto.battle.BattleRoundInfo;
import com.example.sanguogameserver.dto.battle.BattleTreeInfoColumn;
import com.example.sanguogameserver.dto.battle.BattleTreeInfoRow;
import com.example.sanguogameserver.entity.Skill;
import com.example.sanguogameserver.enums.GeneralCategoryEnum;
import com.example.sanguogameserver.enums.SelectTargetTypeEnum;
import com.example.sanguogameserver.enums.TreePlayerTypeEnum;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;


public class AiLogic {

    /**
     * 信息
     */
    public List<BattleTreeInfoRow> trees;

    private TreePlayerTypeEnum treePlayerType;

    private int round;

    /**
     * 所有ai有的地形
     */
    private List<BattleTreeInfoColumn> aiTreeGeneral = new ArrayList<>();

    private List<BattleTreeInfoColumn> enemyTreeGeneral = new ArrayList<>();

    /**
     * 当前行动的ai
     */
    private BattleTreeInfoColumn goTreeInfoColumn;

    /**
     * 当前可行动ai可以攻击到的所有目标
     */
    List<BattleTreeInfoColumn> attackPlayerAlls = new ArrayList<>();
    /**
     * 当前可行动ai可以技能攻击到的所有目标
     */
    List<BattleTreeInfoColumn> skillAttackPlayerAlls = new ArrayList<>();

    // 最终决定攻击的武将
    BattleTreeInfoColumn targetAttackPlayer;
    // 当前回合的攻击技能
    TreeSkill skill;


    public AiLogic(List<BattleTreeInfoRow> trees, TreePlayerTypeEnum treePlayerType) {
        this.trees = trees;
        this.treePlayerType = treePlayerType;
    }

    public BattleRoundInfo aiGo() {
        for (BattleTreeInfoRow treeRow : trees) {
            for (BattleTreeInfoColumn treeInfo : treeRow.getTreeInfoColumns()) {
                if (treeInfo.isAi() && treeInfo.getPlayerTreeGeneral() != null) {
                    aiTreeGeneral.add(treeInfo);
                }
                if (!treeInfo.isAi() && treeInfo.getPlayerTreeGeneral() != null) {
                    enemyTreeGeneral.add(treeInfo);
                }
            }
        }
        // 谁行动
        goTreeInfo();
        System.out.println("行动者:" + goTreeInfoColumn.getPlayerTreeGeneral().getGeneralName());
        if (goTreeInfoColumn == null) {
            // 不行动 FIXME 是否要随机挑一个行动者
            return null;
        }
        BattleRoundInfo battleRoundInfo = new BattleRoundInfo();

        // 回合信息
        AiRoundInfo aiRoundInfo = new AiRoundInfo();
        aiRoundInfo.setPlayerTreeGeneralId(goTreeInfoColumn.getPlayerTreeGeneral().getId());
        aiRoundInfo.setOldRow(goTreeInfoColumn.getRow());
        aiRoundInfo.setOldColumn(goTreeInfoColumn.getColumn());
        // 是否有攻击目标
        if (CollUtil.isNotEmpty(attackPlayerAlls) || CollUtil.isNotEmpty(skillAttackPlayerAlls)) {
            // 选择目标
            if (CollUtil.isNotEmpty(attackPlayerAlls)) {
                selectAttack(attackPlayerAlls, true);
            }
            if (CollUtil.isNotEmpty(skillAttackPlayerAlls)) {
                selectAttack(skillAttackPlayerAlls, false);
            }

            List<CastSkillRespDTO> castSkillRespDTOS = moveAttackPosition(aiRoundInfo);
            battleRoundInfo.setCastSkills(castSkillRespDTOS);
        } else {
            // 没有攻击目标就移动
            moveAttackNeighborhood(aiRoundInfo);
        }

        battleRoundInfo.setAiRoundInfo(aiRoundInfo);
        return battleRoundInfo;
    }

    /**
     * 移动到攻击目标附近
     */
    private void moveAttackNeighborhood(AiRoundInfo aiRoundInfo) {
        TreeGeneral playerTreeGeneral = goTreeInfoColumn.getPlayerTreeGeneral();
        // 坚守原地,
        if (playerTreeGeneral.getAiType() == AiType.stayWhereYouAre) {
            return;
        }
        // 被动攻击,没有找到敌军,不动
        if (playerTreeGeneral.getAiType() == AiType.passiveAttack) {
            return;
        }
        // 寻找最近敌军

        double minDistance = Double.MAX_VALUE;
        BattleTreeInfoColumn nearestEnemy = null;

        int x1 = goTreeInfoColumn.getColumn();
        int y1 = goTreeInfoColumn.getRow();

        for (BattleTreeInfoColumn enemy : enemyTreeGeneral) {
            int x2 = enemy.getColumn();
            int y2 = enemy.getRow();

            // Euclidean distance
            double distance = Math.abs(x2 - x1) + Math.abs(y2 - y1);

            if (distance < minDistance) {
                minDistance = distance;
                nearestEnemy = enemy;
            }
        }

        // nearestEnemy最近的敌人

        // 可以移动的点位
        List<Position> movePosition = moveRange(new Position(goTreeInfoColumn.getColumn(), goTreeInfoColumn.getRow()), playerTreeGeneral.getMoveCount());
        Position nearestEnemyPosition = new Position(nearestEnemy.getColumn(), nearestEnemy.getRow());

        double minMoveDistance = Double.MAX_VALUE;
        Position nearestMovePosition = null;

        for (Position movePos : movePosition) {
            int x3 = movePos.getX();
            int y3 = movePos.getY();

            // Euclidean distance
            double moveDistance = Math.abs(x3 - nearestEnemyPosition.getX()) + Math.abs(y3 - nearestEnemyPosition.getY());

            if (moveDistance < minMoveDistance) {
                minMoveDistance = moveDistance;
                nearestMovePosition = movePos;
            }
        }
        if (nearestMovePosition == null) {
            // 不移动
            return;
        }

        // 交换距离
        BattleTreeInfoColumn moveAttackColumn = trees.get(nearestMovePosition.y).getTreeInfoColumns().get(nearestMovePosition.x);
        playerTreeGeneral.setCurrentMoved(true);
        moveAttackColumn.setPlayerTreeGeneral(playerTreeGeneral);
        moveAttackColumn.setTreePlayerType(goTreeInfoColumn.getTreePlayerType());
        moveAttackColumn.setAi(true);

        goTreeInfoColumn.setPlayerTreeGeneral(null);
        goTreeInfoColumn.setTreePlayerType(null);
        goTreeInfoColumn.setAi(false);

        aiRoundInfo.setOldRow(goTreeInfoColumn.getRow());
        aiRoundInfo.setOldColumn(goTreeInfoColumn.getColumn());
        aiRoundInfo.setNewRow(moveAttackColumn.getRow());
        aiRoundInfo.setNewColumn(moveAttackColumn.getColumn());


    }

    /**
     * 移动攻击点位
     */
    private List<CastSkillRespDTO> moveAttackPosition(AiRoundInfo aiRoundInfo) {
        TreeGeneral aiGeneral = goTreeInfoColumn.getPlayerTreeGeneral();
        TreeGeneral playerTreeGeneral = targetAttackPlayer.getPlayerTreeGeneral();
        // pk,首先选择攻击不到他的位置
        // 先找自己可以攻击的点
        // 先获取目标位置
        Position targetPosition = new Position(targetAttackPlayer.getColumn(), targetAttackPlayer.getRow());
        // 可以攻击的位置
        List<Position> positions;
        if (StrUtil.isNotEmpty(aiGeneral.getRange())) {
            positions = JSON.parseArray(aiGeneral.getRange(), Position.class);
        } else {
            positions = moveRange(new Position(0, 0), aiGeneral.getRangeCount());
        }
        // 可以攻击的点位
        List<Position> attackPositionMove = positions.stream().map(targetPosition::subtract).toList();
        // 可以移动的点位
        List<Position> movePosition = moveRange(new Position(goTreeInfoColumn.getColumn(), goTreeInfoColumn.getRow()), aiGeneral.getMoveCount());
        // 攻击的点位和移动的点位取交集,拿出
        Collection<Position> attackAndMovePositions = CollUtil.intersection(attackPositionMove, movePosition);
        // 判断哪个位置,我能打到他,他打不到我
        // 玩家攻击到的点位
        List<Position> attackPosition = getAttackPosition(playerTreeGeneral.getRange(), playerTreeGeneral.getRangeCount(), targetAttackPlayer);
        Collection<Position> subAttackPosition = CollUtil.subtract(attackAndMovePositions, attackPosition);

        // 可以攻击的位置
        BattleTreeInfoColumn moveAttackColumn = null;
        for (Position subPosition : subAttackPosition) {
            BattleTreeInfoColumn tree = getTree(subPosition);
            if (tree != null && !isObstacles(subPosition) && !isPlayer(tree) && !isTeammate(tree)) {
                moveAttackColumn = tree;
                break;
            }
        }

        if (moveAttackColumn == null) {
            for (Position attackAndMovePosition : attackAndMovePositions) {
                BattleTreeInfoColumn tree = getTree(attackAndMovePosition);
                if (tree != null && !isObstacles(attackAndMovePosition) && !isPlayer(tree) && !isTeammate(tree)) {
                    moveAttackColumn = tree;
                    break;
                }
            }
        }


        aiRoundInfo.setNewRow(moveAttackColumn.getRow());
        aiRoundInfo.setNewColumn(moveAttackColumn.getColumn());
        aiRoundInfo.setSelectRow(targetAttackPlayer.getRow());
        aiRoundInfo.setSelectColumn(targetAttackPlayer.getColumn());
        aiRoundInfo.setTreeSkill(skill);

        // goTreeInfoColumn 移动到 moveAttackColumn
        if (moveAttackColumn.getRow() != goTreeInfoColumn.getRow() || moveAttackColumn.getColumn() != goTreeInfoColumn.getColumn()) {
            moveAttackColumn.setPlayerTreeGeneral(aiGeneral);
            moveAttackColumn.setTreePlayerType(goTreeInfoColumn.getTreePlayerType());
            moveAttackColumn.setAi(true);

            goTreeInfoColumn.setPlayerTreeGeneral(null);
            goTreeInfoColumn.setTreePlayerType(null);
            goTreeInfoColumn.setAi(false);
        }


        // 攻击
        List<CastSkillRespDTO> attack = AttackLogic.attack(moveAttackColumn, targetAttackPlayer, trees, skill);
        for (CastSkillRespDTO castSkillRespDTO : attack) {
            // 反击
            List<CastSkillRespDTO> counterattackCastSkill = AttackLogic.attack(castSkillRespDTO.getAttackTarget(), moveAttackColumn, trees, AttackLogic.normalAttack(castSkillRespDTO.getAttackTarget().getPlayerTreeGeneral(), moveAttackColumn.getPlayerTreeGeneral()));
            castSkillRespDTO.setCounterattackCastSkill(counterattackCastSkill);
        }
        return attack;
    }

    private void selectAttack(List<BattleTreeInfoColumn> attackPlayerAlls, boolean isNormalAttack) {
        targetAttackPlayer = attackPlayerAlls.getFirst();
        // 当前行动的ai
        TreeGeneral aiGeneral = goTreeInfoColumn.getPlayerTreeGeneral();
        // 先计算ai的攻击力n;kl

        skill = AttackLogic.normalAttack(aiGeneral, targetAttackPlayer.getPlayerTreeGeneral());
        // 选择攻击目标
        for (BattleTreeInfoColumn attackPlayer : attackPlayerAlls) {
            // 默认分数0
            int beAttackScore = 0;
            TreeGeneral playGeneral = attackPlayer.getPlayerTreeGeneral();
            TreeSkill attack = null;
            if (isNormalAttack) {
                attack = AttackLogic.normalAttack(aiGeneral, attackPlayer.getPlayerTreeGeneral());
            } else {
                attack = AttackLogic.attack(aiGeneral, attackPlayer.getPlayerTreeGeneral());
            }


            int currentAttack = Math.max(attack.getNormalDamage(), 1);

            // 是否可以ko对方,
            // 先用最大的伤害技能试试,法师,还没做技能,回来再试?
            Integer currentHealth = playGeneral.getCurrentHealth();
            Integer health = playGeneral.getHealth();
            double health10Rage = health * 0.1;
            if (currentAttack >= currentHealth) {
                beAttackScore += 78;
            } else if (currentAttack > health10Rage && ((double) currentHealth / health) < 0.41) {
                // 攻击占总hp的10% 并且怪物血量在41%下
                beAttackScore += 28;
            } else if (currentAttack > health10Rage && ((double) currentHealth / health) > 0.41) {
                // 攻击占总hp的10% 并且怪物血量在41%以上
                beAttackScore += 14;
            } else if (currentAttack < health10Rage && ((double) currentHealth / health) < 0.41) {
                // 攻击不占总hp的10% 并且怪物血量在41%下
                beAttackScore += 14;
            } else if (currentAttack < health10Rage && ((double) currentHealth / health) > 0.41) {
                // 攻击不占总hp的10% 并且怪物血量在41%上
                beAttackScore += 8;
            }

            if (playGeneral.getBuffer().contains(GeneralStatusType.chaos)) {
                beAttackScore += 8;
            }

            attackPlayer.setBeAttackScore(beAttackScore);
            if (targetAttackPlayer.getBeAttackScore() < attackPlayer.getBeAttackScore()) {
                targetAttackPlayer = attackPlayer;
                skill = attack;
            }
        }
    }


    public void goTreeInfo() {
        // 恢复地形 70 残血 60 法师 射手 50   战士 40 牧师 30
        goTreeInfoColumn = null;
        for (BattleTreeInfoColumn treeInfoColumn : aiTreeGeneral) {
            // 移动过的武将不再移动
            if (treeInfoColumn.getPlayerTreeGeneral().isCurrentMoved()) {
                continue;
            }
            int score = 0;
            if (treeInfoColumn.getTreeType() == TreeType.city) {
                score += 10;
            }
            float healthRate = (float) treeInfoColumn.getPlayerTreeGeneral().getCurrentHealth() / (float) treeInfoColumn.getPlayerTreeGeneral().getHealth();
            if (healthRate <= SanGuoConst.residualBlood) {
                score += 7;
            }
            if (treeInfoColumn.getPlayerTreeGeneral().getGeneralCategoryEnum() == GeneralCategoryEnum.mage || treeInfoColumn.getPlayerTreeGeneral().getGeneralCategoryEnum() == GeneralCategoryEnum.shooter) {
                score += 5;
            }
            if (treeInfoColumn.getPlayerTreeGeneral().getGeneralCategoryEnum() == GeneralCategoryEnum.shieldSoldier || treeInfoColumn.getPlayerTreeGeneral().getGeneralCategoryEnum() == GeneralCategoryEnum.warrior) {
                score += 2;
            }
            if (treeInfoColumn.getPlayerTreeGeneral().getGeneralCategoryEnum() == GeneralCategoryEnum.priest) {
                score += 1;
            }

            List<BattleTreeInfoColumn> treeInfoColumns = moveTreeRange(treeInfoColumn);
            // 加入自身位置
            treeInfoColumns.addFirst(treeInfoColumn);
            List<BattleTreeInfoColumn> attackPlayerAlls = new ArrayList<>();
            List<BattleTreeInfoColumn> skillAttackPlayerAlls = new ArrayList<>();
            for (BattleTreeInfoColumn column : treeInfoColumns) {
                // 普通攻击范围
                List<BattleTreeInfoColumn> attackPlayer = getAttackPlayer(treeInfoColumn.getPlayerTreeGeneral().getRange(), treeInfoColumn.getPlayerTreeGeneral().getRangeCount(), column);
                attackPlayerAlls.addAll(attackPlayer);

                // 技能攻击范围
                if (isEnemySkill(treeInfoColumn.getPlayerTreeGeneral().getSkills())) {
                    List<BattleTreeInfoColumn> skillAttackPlayer = getAttackPlayer(null, treeInfoColumn.getPlayerTreeGeneral().getSkillRangeCount(), column);
                    skillAttackPlayerAlls.addAll(skillAttackPlayer);
                }
            }


            // 附件没有敌军
            if (treeInfoColumn.getPlayerTreeGeneral().getAiType() == AiType.passiveAttack) {
                if (CollUtil.isEmpty(attackPlayerAlls) && CollUtil.isEmpty(skillAttackPlayerAlls)) {
                    score = 0;
                }
            }

            treeInfoColumn.setGoScore(score);
            if (goTreeInfoColumn == null || treeInfoColumn.getGoScore() > goTreeInfoColumn.getGoScore()) {
                goTreeInfoColumn = treeInfoColumn;
                this.attackPlayerAlls = attackPlayerAlls;
            }
        }
    }

    public List<BattleTreeInfoColumn> getAttackPlayer(String rangeJson, int rangeCount, BattleTreeInfoColumn treeInfoColumn) {
        List<Position> positions = getAttackPosition(rangeJson, rangeCount, treeInfoColumn);
        List<BattleTreeInfoColumn> treeInfoColumns = new ArrayList<>();
        for (Position position : positions) {
            BattleTreeInfoColumn tree = getTree(position);
            if (tree != null && isPlayer(tree)) {
                treeInfoColumns.add(tree);
            }
        }
        return treeInfoColumns;
    }


    /**
     * 所有可以攻击的点
     *
     * @param rangeJson
     * @param rangeCount
     * @param treeInfoColumn
     * @return
     */
    public List<BattleTreeInfoColumn> getAttackRange(String rangeJson, int rangeCount, BattleTreeInfoColumn treeInfoColumn) {
        List<Position> positions = getAttackPosition(rangeJson, rangeCount, treeInfoColumn);
        List<BattleTreeInfoColumn> treeInfoColumns = new ArrayList<>();
        for (Position position : positions) {
            BattleTreeInfoColumn tree = getTree(position);
            if (tree != null) {
                treeInfoColumns.add(tree);
            }
        }
        return treeInfoColumns;
    }


    public List<Position> getAttackPosition(String rangeJson, int rangeCount, BattleTreeInfoColumn treeInfoColumn) {
        List<Position> positions;
        if (StrUtil.isNotEmpty(rangeJson)) {
            positions = JSON.parseArray(rangeJson, Position.class);
            for (Position position : positions) {
                position.x += treeInfoColumn.getColumn();
                position.y += treeInfoColumn.getRow();
            }
        } else {
            Position currentposition = new Position();
            currentposition.setX(treeInfoColumn.getColumn());
            currentposition.setY(treeInfoColumn.getRow());
            positions = moveRange(currentposition, rangeCount);
        }
        return positions;
    }


    /**
     * 返回所有可移动的格子
     *
     * @param treeInfoColumn
     * @return
     */
    public List<BattleTreeInfoColumn> moveTreeRange(BattleTreeInfoColumn treeInfoColumn) {
        Integer x = treeInfoColumn.getColumn();
        Integer y = treeInfoColumn.getRow();
        Position currentposition = new Position();
        currentposition.setX(x);
        currentposition.setY(y);

        List<Position> positions = moveRange(currentposition, treeInfoColumn.getPlayerTreeGeneral().getMoveCount());
        List<BattleTreeInfoColumn> treeInfoColumns = new ArrayList<>();
        for (Position movePosition : positions) {
            BattleTreeInfoColumn tree = getTree(movePosition);
            if (tree != null && !isObstacles(tree) && !isEnemy(tree)) {
                treeInfoColumns.add(tree);
            }
        }

        return treeInfoColumns;
    }


    /**
     * 返回可移动范围
     *
     * @param currentPosition
     * @param range
     * @return
     */
    public List<Position> moveRange(Position currentPosition, int range) {

        List<Position> oldPos = new ArrayList<>();
        //oldPos.add(currentPosition);
        List<Position> newPos = new ArrayList<Position>();

        if (!isObstacles(currentPosition.add(Position.up))) {
            newPos.add(currentPosition.add(Position.up));
        }

        if (!isObstacles(currentPosition.add(Position.left))) {
            newPos.add(currentPosition.add(Position.left));
        }

        if (!isObstacles(currentPosition.add(Position.down))) {
            newPos.add(currentPosition.add(Position.down));
        }

        if (!isObstacles(currentPosition.add(Position.right))) {
            newPos.add(currentPosition.add(Position.right));
        }


        for (int i = 0; i < range; i++) {
            newPos = moveDiffusion(oldPos, newPos);
        }
        oldPos.add(currentPosition);
        return oldPos.stream().distinct().toList();
    }

    public List<Position> moveDiffusion(List<Position> oldPos, List<Position> newPos) {
        List<Position> newPosV = new ArrayList<>();
        for (Position pos : newPos) {
            if (!isTeammate(pos)) {
                oldPos.add(pos);
            }
            if (isEnemy(pos)) {
                continue;
            }

            // 检测上方是否有障碍物
            if (!isObstacles(pos.add(Position.up))) {
                newPosV.add(pos.add(Position.up));
            }

            if (!isObstacles(pos.add(Position.left))) {
                newPosV.add(pos.add(Position.left));
            }

            if (!isObstacles(pos.add(Position.down))) {
                newPosV.add(pos.add(Position.down));
            }

            if (!isObstacles(pos.add(Position.right))) {
                newPosV.add(pos.add(Position.right));
            }
        }
        return newPosV;
    }

    /**
     * 是否有能选中敌人的技能
     *
     * @return
     */
    private boolean isEnemySkill(List<TreeSkill> skills) {
        if (CollUtil.isEmpty(skills)) {
            return false;
        }
        for (TreeSkill treeSkill : skills) {
            Skill skill = treeSkill.getSkill();
            String targetType = skill.getTargetType();
            if (targetType.contains(SelectTargetTypeEnum.enemy.getValue())) {
                return true;
            }
        }
        return false;
    }

    // 是否有障碍
    private boolean isObstacles(Position position) {
        BattleTreeInfoColumn treeInfoColumn = getTree(position);
        if (treeInfoColumn == null) {
            return true;
        }
        return isObstacles(treeInfoColumn);
    }

    private boolean isObstacles(BattleTreeInfoColumn treeInfoColumn) {
        return !treeInfoColumn.getIsMove();
    }

    private boolean isTeammate(Position position) {
        BattleTreeInfoColumn treeInfoColumn = getTree(position);
        if (treeInfoColumn == null) {
            return true;
        }
        return isTeammate(treeInfoColumn);
    }

    private boolean isTeammate(BattleTreeInfoColumn treeInfoColumn) {
        return false;
    }

    // 是否有敌人
    private boolean isEnemy(Position position) {
        // 是否有玩家
        return isEnemy(getTree(position)) || isEnemy(getTree(position.add(Position.up))) || isEnemy(getTree(position.add(Position.left))) || isEnemy(getTree(position.add(Position.down))) || isEnemy(getTree(position.add(Position.right)));
    }

    private boolean isEnemy(BattleTreeInfoColumn treeInfoColumn) {
        if (treeInfoColumn != null && treeInfoColumn.getTreePlayerType() != null && treeInfoColumn.getTreePlayerType() != this.treePlayerType) {
            return true;
        }
        return false;
    }

    private BattleTreeInfoColumn getTree(Position position) {
        if (isNotOutOfBounds(position)) {
            return trees.get(position.y).getTreeInfoColumns().get(position.x);
        }
        return null;
    }

    private boolean isNotOutOfBounds(Position position) {
        if (position.x < 0 || position.y < 0) {
            return false;
        }
        return trees.size() > position.y && trees.get(position.y).getTreeInfoColumns().size() > position.x;
    }

    private boolean isPlayer(BattleTreeInfoColumn treeInfoColumn) {
        return treeInfoColumn != null && treeInfoColumn.getPlayerTreeGeneral() != null && !treeInfoColumn.getPlayerTreeGeneral().isAi();
    }


}
